/*
有 N个物品和一个容量是 V的背包。
物品之间具有依赖关系，且依赖关系组成一棵树的形状。如果选择一个物品，则必须选择它的父节点。
每件物品的编号是 i，体积是 vi，价值是 wi，依赖的父节点编号是 pi。
物品的下标范围是 1…N。
求解将哪些物品装入背包，可使物品总体积不超过背包容量，且总价值最大。
输出最大价值。

输入格式
第一行有两个整数 N，V，用空格隔开，分别表示物品个数和背包容量。

接下来有 N行数据，每行数据表示一个物品。
第 i行有三个整数 vi,wi,pi，用空格隔开，分别表示物品的体积、价值和依赖的物品编号。
如果 pi=−1，表示根节点。 
数据保证所有物品构成一棵树。
*/
#include<bits/stdc++.h>
#define endl "\n"
#define ll long long
#define all(rq) rq.begin(),rq.end()
#define max(a,b) (a<b?b:a)
#define min(a,b) (a<b?a:b)
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;
const int INF=0x3f3f3f3f;
vector<vector<int>> tree;
vector<vector<int>> f; //f[i][j]以i为根节点的子树在j容量下能获得的最大价值
vector<int> v,w,p;
int n,m;

void dfs(int root){
	for(int j=v[root];j<=m;j++){ //该子树必然选择该节点本身
		f[root][j]=w[root];
	} 
	
	for(int i=0;i<tree[root].size();i++){ //遍历根节点所有子节点
		dfs(tree[root][i]); //先处理该子节点
		int son=tree[root][i];
		
		for(int j=m;j>=v[root];j--){ //遍历所有需要处理的状态
			for(int k=0;k<=j-v[root];k++){ //我若不选择当前子节点便是f[root][j]本身 我若选择当前子节点并为当前子节点分配k的空间 那么便是当前树为自身和已经遍历的其他节点分配j-k空间能获得最大价值加上以son为根节点的子树在k空间下能获得的最大价值之和
				f[root][j]=max(f[root][j],f[root][j-k]+f[son][k]);
			}
		}
	}
	return;
}

int main(){
	IOS;
	cin>>n>>m;
	tree.resize(n+1);
	f.resize(n+1,vector<int>(m+1,-1));
	
	v.resize(n+1);
	w.resize(n+1);
	p.resize(n+1);
	
	int root=-1;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i]>>p[i];
		
		if(p[i]==-1){
			root=i;
		}else{
			tree[p[i]].push_back(i);
		}
	}
	
	dfs(root);
	
	int ans=0;
	for(int j=1;j<=m;j++){
		ans=max(ans,f[root][j]);
	}
	cout<<ans<<endl;
	return 0;
}